﻿using System;
using System.Diagnostics;
using System.Threading;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;

namespace MugenInjection.Core
{
    /// <summary>
    /// An object that notifies when it is disposed.
    /// </summary>
    public abstract class DisposableObject : IDisposableObject
    {
        #region Fields

        private readonly object _locker;
        private bool _isDisposed;
        private bool _isDisposing;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="DisposableObject"/> class.
        /// </summary>
        protected DisposableObject()
        {
            _locker = new object();
        }

        #endregion

        #region Destructor

        ~DisposableObject()
        {
            Dispose(false);
#if !SILVERLIGHT
            Debug.WriteLine(string.Format("GC disposed object {0}!!!", InjectorUtilsInternal.FormatType(GetType())));
#endif
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
#if !SILVERLIGHT
            Debug.WriteLine(string.Format("Disposed object {0}.", InjectorUtilsInternal.FormatType(GetType())));
#endif
            lock (_locker)
            {
                if (_isDisposing || _isDisposed)
                    return;
                _isDisposing = true;
            }
            GC.SuppressFinalize(this);
            Dispose(true);
            OnDisposed();
            ClearEvents();
            _isDisposed = true;
        }

        #endregion

        #region Method

        protected void EnsureIsNotDisposed()
        {
            if (_isDisposed)
                throw new ObjectDisposedException(GetType().FullName,
                                                  string.Format("The current object {0} is disposed.", InjectorUtilsInternal.FormatType(GetType())));
        }

        /// <summary>
        /// Releases resources held by the object.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
        }

        private void OnDisposed()
        {
            Action<IDisposableObject> handler = Disposed;
            if (handler != null) handler(this);
        }

        private void ClearEvents()
        {
            if (Disposed == null) return;
            foreach (Action<IDisposableObject> @delegate in Disposed.GetInvocationList())
            {
                if (@delegate == null) continue;
                Disposed -= @delegate;
            }
        }

        #endregion

        #region Implementation of IDisposableObject

        /// <summary>
        /// Gets a value indicating whether this instance is disposed.
        /// </summary>
        public bool IsDisposed
        {
            get
            {
                lock (_locker)
                    return _isDisposed;
            }
        }

        /// <summary>
        /// Occured after disposed current <see cref="IDisposableObject"/>.
        /// </summary>
        public event Action<IDisposableObject> Disposed;

        #endregion
    }
}